import time
import platform
import sys
from typing import Optional, List, Callable
import serial
import serial.tools.list_ports
from global_settings import debug_print

# 平台特定的导入
if platform.system() == "Windows":
    import winreg
elif platform.system() == "Darwin":  # macOS
    import glob
    import os

class SerialManager:
    def __init__(self, log_callback: Optional[Callable] = None):
        self.serial_port: Optional[serial.Serial] = None
        self.log_callback = log_callback
        self._last_read_time = 0
        self._read_timeout = 0.5
        self._is_reading = False
        self._stop_reading = False
        self.system_platform = platform.system()

    def get_available_ports(self) -> List[str]:
        """
        跨平台获取可用串口列表
        """
        ports = []

        try:
            # 方法1: 使用pyserial的list_ports获取物理串口（跨平台）
            available_ports = serial.tools.list_ports.comports()
            for port in available_ports:
                if port.device:
                    ports.append(port.device)
                    debug_print(f"检测到串口: {port.device} - {port.description}")

            # 方法2: 平台特定的补充检测
            platform_ports = self._get_platform_specific_ports()
            for port in platform_ports:
                if port not in ports:
                    ports.append(port)
                    debug_print(f"平台特定检测到串口: {port}")

            # 去重并排序
            ports = list(set(ports))
            ports = self._sort_ports(ports)

            debug_print(f"最终可用串口列表: {ports}")

        except Exception as e:
            debug_print(f"获取串口列表时出错: {e}")
            # 回退到基本方法
            ports = self._get_fallback_ports()

        return ports

    def _get_platform_specific_ports(self) -> List[str]:
        """
        平台特定的串口检测
        """
        ports = []
        
        if self.system_platform == "Windows":
            ports.extend(self._get_windows_ports())
        elif self.system_platform == "Darwin":  # macOS
            ports.extend(self._get_macos_ports())
        elif self.system_platform == "Linux":
            ports.extend(self._get_linux_ports())
            
        return ports

    def _get_windows_ports(self) -> List[str]:
        """Windows平台特定检测"""
        ports = []
        try:
            # 从注册表检测虚拟串口
            virtual_ports = self._get_windows_virtual_ports()
            ports.extend(virtual_ports)
            
            # 检测常用COM端口范围
            for i in range(1, 257):  # 扩展到COM256
                port_name = f"COM{i}"
                if port_name not in ports:
                    try:
                        test_port = serial.Serial(port_name)
                        test_port.close()
                        ports.append(port_name)
                    except:
                        pass
        except Exception as e:
            debug_print(f"Windows端口检测错误: {e}")
            
        return ports

    def _get_windows_virtual_ports(self) -> List[str]:
        """Windows虚拟串口检测"""
        virtual_ports = []
        try:
            # 从COM Name Arbiter获取
            key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,
                               r"SYSTEM\CurrentControlSet\Control\COM Name Arbiter")
            i = 0
            while True:
                try:
                    value_name, value_data, _ = winreg.EnumValue(key, i)
                    if value_name.startswith("COM"):
                        virtual_ports.append(value_name)
                    i += 1
                except WindowsError:
                    break
            winreg.CloseKey(key)
        except Exception as e:
            debug_print(f"Windows虚拟串口检测错误: {e}")
            
        return virtual_ports

    def _get_linux_ports(self) -> List[str]:
        """Linux平台特定检测"""
        ports = []
        try:
            # 检测常见的Linux串口设备
            common_patterns = [
                "/dev/ttyS*",  # 原生串口
                "/dev/ttyUSB*",  # USB转串口
                "/dev/ttyACM*",  # ACM设备
                "/dev/ttyAMA*",  # AMA设备（树莓派等）
            ]
            
            for pattern in common_patterns:
                try:
                    import glob
                    found_ports = glob.glob(pattern)
                    for port in found_ports:
                        if os.path.exists(port):
                            ports.append(port)
                except:
                    pass
                    
        except Exception as e:
            debug_print(f"Linux端口检测错误: {e}")
            
        return ports

    def _get_macos_ports(self) -> List[str]:
        """macOS平台特定检测"""
        ports = []
        try:
            # 检测macOS常见的串口设备
            common_patterns = [
                "/dev/cu.*",  # 调用设备
                "/dev/tty.*",  # 终端设备
            ]
            
            for pattern in common_patterns:
                try:
                    found_ports = glob.glob(pattern)
                    for port in found_ports:
                        # 过滤掉不需要的设备
                        if any(exclude in port for exclude in [".Bluetooth", ".iPad", ".iPhone"]):
                            continue
                        if os.path.exists(port):
                            ports.append(port)
                except:
                    pass
                    
        except Exception as e:
            debug_print(f"macOS端口检测错误: {e}")
            
        return ports

    def _sort_ports(self, ports: List[str]) -> List[str]:
        """跨平台端口排序"""
        def port_key(port_name):
            # Windows: COM1, COM2, ...
            if port_name.upper().startswith("COM"):
                try:
                    return (0, int(port_name[3:]))
                except:
                    return (0, float('inf'))
            # Linux/macOS: /dev/ttyUSB0, /dev/ttyS0, etc.
            elif "/dev/" in port_name:
                return (1, port_name)
            else:
                return (2, port_name)
                
        return sorted(ports, key=port_key)

    def _get_fallback_ports(self) -> List[str]:
        """获取回退端口列表"""
        if self.system_platform == "Windows":
            return [f"COM{i}" for i in range(1, 21)]
        elif self.system_platform == "Linux":
            return ["/dev/ttyS0", "/dev/ttyS1", "/dev/ttyUSB0", "/dev/ttyUSB1"]
        elif self.system_platform == "Darwin":
            return ["/dev/cu.usbserial", "/dev/cu.Bluetooth-Incoming-Port"]
        else:
            return []

    def connect(self, port: str, baudrate: int = 9600, bytesize: int = 8,
                stopbits: float = 1, parity: str = 'N', timeout: float = 1.0) -> None:
        """跨平台串口连接"""
        try:
            debug_print(f"正在连接串口: {port}, 波特率: {baudrate}")

            # 平台特定的连接参数调整
            connection_params = {
                'port': port,
                'baudrate': baudrate,
                'bytesize': bytesize,
                'stopbits': stopbits,
                'parity': parity,
                'timeout': timeout,
                'write_timeout': 1.0
            }

            # Linux/macOS特定设置
            if self.system_platform in ["Linux", "Darwin"]:
                connection_params.update({
                    'rtscts': False,  # 禁用RTS/CTS流控
                    'dsrdtr': False,  # 禁用DSR/DTR流控
                    'xonxoff': False,  # 禁用软件流控
                })

            self.serial_port = serial.Serial(**connection_params)

            # 清空缓冲区
            self.serial_port.reset_input_buffer()
            self.serial_port.reset_output_buffer()

            debug_print(f"串口连接成功: {port}")

        except Exception as e:
            error_msg = f"串口连接失败: {str(e)}"
            # 平台特定的错误处理建议
            if self.system_platform == "Linux" and "PermissionError" in str(e):
                error_msg += "\n提示: 在Linux上可能需要sudo权限或添加用户到dialout组"
            elif self.system_platform == "Darwin" and "PermissionError" in str(e):
                error_msg += "\n提示: 在macOS上可能需要权限配置"
                
            raise Exception(error_msg)

    def disconnect(self) -> None:
        """断开串口连接"""
        if self.serial_port and self.serial_port.is_open:
            try:
                self.serial_port.close()
                debug_print("串口已断开")
            except Exception as e:
                debug_print(f"断开串口时出错: {e}")
        self.serial_port = None

    def is_connected(self) -> bool:
        """检查是否已连接"""
        return self.serial_port is not None and self.serial_port.is_open

    def read_data(self, timeout: Optional[float] = None, expected_length: Optional[int] = None) -> Optional[bytes]:
        """读取数据（保持原有逻辑不变）"""
        if not self.is_connected():
            return None

        try:
            original_timeout = self.serial_port.timeout

            if timeout is not None:
                self.serial_port.timeout = timeout

            data = None

            if expected_length is not None:
                data = self.serial_port.read(expected_length)
            else:
                if self.serial_port.in_waiting > 0:
                    data = self.serial_port.read(self.serial_port.in_waiting)
                else:
                    data = self.serial_port.read(1)
                    if data and self.serial_port.in_waiting > 0:
                        data += self.serial_port.read(self.serial_port.in_waiting)

            if timeout is not None:
                self.serial_port.timeout = original_timeout

            if data and self.log_callback:
                self.log_callback("Rx", data)

            self._last_read_time = time.time()
            return data if data else None

        except serial.SerialTimeoutException:
            debug_print("串口读取超时")
            return None
        except Exception as e:
            debug_print(f"数据读取错误: {str(e)}")
            return None

    def write_data(self, data: bytes) -> bool:
        """写入数据（保持原有逻辑不变）"""
        if not self.is_connected():
            debug_print("串口未连接，无法写入数据")
            return False

        try:
            self.serial_port.reset_input_buffer()
            bytes_written = self.serial_port.write(data)
            self.serial_port.flush()

            if self.log_callback:
                self.log_callback("Tx", data)

            debug_print(f"成功写入 {bytes_written} 字节数据")
            return bytes_written == len(data)

        except serial.SerialTimeoutException:
            debug_print("串口写入超时")
            return False
        except Exception as e:
            debug_print(f"数据写入错误: {str(e)}")
            return False

    # 以下方法保持原有逻辑不变
    def send_command(self, command: str) -> bool:
        data_to_send = f"{command}\r\n".encode()
        return self.write_data(data_to_send)

    def send_hex_command(self, hex_data: str) -> bool:
        try:
            hex_clean = hex_data.replace(" ", "").replace("\r", "").replace("\n", "")
            if len(hex_clean) % 2 != 0:
                debug_print("十六进制数据长度不正确")
                return False

            data_bytes = bytes.fromhex(hex_clean)
            return self.write_data(data_bytes)

        except ValueError as e:
            debug_print(f"十六进制数据格式错误: {e}")
            return False

    def get_port_info(self) -> dict:
        if not self.is_connected():
            return {}

        return {
            'port': self.serial_port.port,
            'baudrate': self.serial_port.baudrate,
            'bytesize': self.serial_port.bytesize,
            'stopbits': self.serial_port.stopbits,
            'parity': self.serial_port.parity,
            'timeout': self.serial_port.timeout,
            'platform': self.system_platform
        }

    def set_timeout(self, timeout: float) -> None:
        if self.is_connected():
            self.serial_port.timeout = timeout
            self._read_timeout = timeout

    def flush_buffers(self) -> None:
        if self.is_connected():
            self.serial_port.reset_input_buffer()
            self.serial_port.reset_output_buffer()
            debug_print("串口缓冲区已清空")

    def wait_for_data(self, timeout: float = 1.0) -> bool:
        if not self.is_connected():
            return False

        start_time = time.time()
        while time.time() - start_time < timeout:
            if self.serial_port.in_waiting > 0:
                return True
            time.sleep(0.01)

        return False

    def __del__(self):
        self.disconnect()